#pragma once

extern void XcpMarkWeakPointer(void * thisPtr, void * ptrToWeakPtr);

template<typename T>
class xvector;

template<typename T>
class _xvector_base_const_iterator
{
public: 
	_xvector_base_const_iterator(XUINT32 uptoIndex, const xvector<T> * container)
	{
		this->m_Upto = uptoIndex;
		this->m_Container = container;
		XcpMarkWeakPointer(this, &this->m_Container);
		XASSERT(this->IsEndIterator() || (uptoIndex <= container->size()));
	}

	_xvector_base_const_iterator()
	{
		this->m_Upto = 0xFFFFFFFFu;
		this->m_Container = NULL;
		XcpMarkWeakPointer(this, &this->m_Container);
	}

	T const & operator*() const
	{
		XASSERT(this->m_Container != NULL);
		XASSERT(this->m_Upto < this->m_Container->size());

		return this->m_Container->TypedPointerAt(this->m_Upto);
	}

	T const * operator->() const
	{
		XASSERT(this->m_Container != NULL);
		XASSERT(this->m_Upto < this->m_Container->size());

		return this->operator*();
	}
protected: 
	XBOOL IsEndIterator() const
	{
		return this->m_Upto == 0xFFFFFFFF;
	}
	XBOOL AreEqual(const _xvector_base_const_iterator<T> & rhs) const
	{
		XASSERT(this->m_Container == rhs.m_Container);
		XASSERT(this->m_Container != NULL);
		return this->m_Upto == rhs->m_Upto;
	}

	XBOOL AreNotEqual(const _xvector_base_const_iterator<T> & rhs) const
	{
		return !this->AreEqual(rhs);
	}

	void AssignToThis(const _xvector_base_const_iterator<T> & rhs)
	{
		if ( this != rhs )
		{
			this->m_Upto = rhs->m_Upto;
			this->m_Container = rhs->m_Container;
		}
	}

	XUINT32 m_Upto;
	const xvector<T> * m_Container;
};

template<typename T>
class _xvector_const_iterator :
	public _xvector_base_const_iterator<T>
{
public: 
	_xvector_const_iterator(XUINT32 uptoIndex, const xvector<T> * container) :
		_xvector_base_const_iterator<T>(uptoIndex,container)
	{

	}

	_xvector_const_iterator() :
		_xvector_base_const_iterator<T>()
	{

	}

	_xvector_const_iterator<T> operator++(XINT32)
	{
		XASSERT(!this->IsEndIterator());

		_xvector_const_iterator<T> bk;
		bk.m_Upto = this.m_Upto;
		bk.m_Container = this.m_Container;

		_xvector_const_iterator<T> ret = this->operator++();

		ret.m_Upto = bk.m_Upto;
		ret.m_Container = bk.m_Container;

		return ret
	}


	_xvector_const_iterator<T> & operator++()
	{
		XASSERT(!this->IsEndIterator());
		XASSERT(this->m_Container != NULL);
		if(this->m_Upto < this->m_Container->size())
			++this->m_Upto;
		return this;
	}
	_xvector_const_iterator<T> & operator+=(XINT32)
	{
		NOTIMPL
	}

	XBOOL operator==(const _xvector_const_iterator<T> & rhs)
	{
		return this->AreEqual(rhs);
	}

	XBOOL operator!=(const _xvector_const_iterator<T> & rhs)
	{
		return this->AreNotEqual(rhs);
	}

	_xvector_const_iterator<T> & operator=(const _xvector_const_iterator<T> & rhs)
	{
		this->AssignToThis(rhs);
	}
};


template <typename T>
class _xvector_iterator :
	public _xvector_const_iterator<T>
{
public: 
	_xvector_iterator(XUINT32 uptoIndex, xvector<T> * container) :
		_xvector_const_iterator<T>(uptoIndex,container)
	{

	}

	_xvector_iterator() :
		_xvector_const_iterator<T>()
	{

	}

	T & operator*() const
	{
		XASSERT(this->m_Container != NULL);
		XASSERT(this->m_Upto < this->m_Container->size());

		return this->m_Container->TypedPointerAt(this->m_Upto);
	}

	T * operator->() const
	{
		XASSERT(this->m_Container != NULL);
		XASSERT(this->m_Upto < this->m_Container->size());

		return this->operator*();
	}

	_xvector_iterator<T> operator++(XINT32)
	{

		_xvector_iterator<T> bk;
		bk.m_Upto = this.m_Upto;
		bk.m_Container = this.m_Container;

		_xvector_iterator<T> ret = this->operator++();

		ret.m_Upto = bk.m_Upto;
		ret.m_Container = bk.m_Container;

		return ret
	}

	_xvector_iterator<T> & operator++()
	{
		return _xvector_const_iterator<T>::operator ++();
	}

	_xvector_iterator<T> & operator+=(XINT32)
	{
		NOTIMPL
	}
};


template<typename T>
class _xvector_const_reverse_iterator :
	public _xvector_base_const_iterator<T>
{
public: 
	_xvector_const_reverse_iterator(XUINT32 uptoIndex, const xvector<T> * container)
	{
		NOTIMPL
	}

	_xvector_const_reverse_iterator()
	{
		NOTIMPL
	}

	_xvector_const_reverse_iterator<T> operator++(XINT32)
	{
		XASSERT(!this->IsEndIterator());

		_xvector_const_reverse_iterator<T> bk;
		bk.m_Upto = this.m_Upto;
		bk.m_Container = this.m_Container;

		_xvector_const_reverse_iterator<T> ret = this->operator++();

		ret.m_Upto = bk.m_Upto;
		ret.m_Container = bk.m_Container;
	}

	_xvector_const_reverse_iterator<T> & operator++()
	{
		XASSERT(!this->IsEndIterator());
		XASSERT(this->m_Container != NULL);

		if(!this->IsEndIterator())
		{
			this->m_Upto -= 1;
		}
		return this;
	}

	_xvector_const_reverse_iterator<T> & operator+=(XINT32)
	{
		NOTIMPL
	}

	_xvector_const_reverse_iterator<T> operator--(XINT32)
	{
		NOTIMPL
	}

	_xvector_const_reverse_iterator<T> & operator--()
	{
		NOTIMPL
	}

	_xvector_const_reverse_iterator<T> & operator-=(XINT32)
	{
		NOTIMPL
	}

	XUINT32 operator==(const _xvector_const_reverse_iterator<T> &)
	{
		return this->AreEqual(rhs);
	}

	XUINT32 operator!=(const _xvector_const_reverse_iterator<T> & rhs) const
	{
		return this->AreNotEqual(rhs);
	}

	_xvector_const_reverse_iterator<T> & operator=(const _xvector_const_reverse_iterator<T> &)
	{
		this->AssignToThis(rhs);
	}
};




template<typename T>
class _xvector_reverse_iterator :
	public _xvector_const_reverse_iterator<T>
{
public: 
	_xvector_reverse_iterator(XUINT32 uptoIndex, xvector<T> * container)
	{
		NOTIMPL
	}

	_xvector_reverse_iterator()
	{
		NOTIMPL
	}

	T & operator*() const
	{
		XASSERT(this->m_Container != NULL);
		XASSERT(this->m_Upto < this->m_Container->size());

		return this->m_Container->TypedPointerAt(this->m_Upto);
	}

	T * operator->() const
	{
		XASSERT(this->m_Container != NULL);
		XASSERT(this->m_Upto < this->m_Container->size());

		return this->operator*();
	}

	_xvector_reverse_iterator<T> operator++(XINT32)
	{
		_xvector_reverse_iterator<T> bk;
		bk.m_Upto = this.m_Upto;
		bk.m_Container = this.m_Container;

		_xvector_reverse_iterator<T> ret = this->operator++();

		ret.m_Upto = bk.m_Upto;
		ret.m_Container = bk.m_Container;
	}

	_xvector_reverse_iterator<T> & operator++()
	{
		return _xvector_const_reverse_iterator<T>::operator++() ;
	}

	_xvector_reverse_iterator<T> & operator+=(XINT32)
	{
		NOTIMPL
	}

	_xvector_reverse_iterator<T> operator--(XINT32)
	{
		NOTIMPL
	}

	_xvector_reverse_iterator<T> & operator--()
	{
		NOTIMPL
	}

	_xvector_reverse_iterator<T> & operator-=(XINT32)
	{
		NOTIMPL
	}
};


template<typename T>
class _xvector_base
{
private: 
	_xvector_base & operator=(const _xvector_base<T> &)
	{
		NOTIMPL
	}

public: 
	_xvector_base(XUINT32)
	{
		NOTIMPL
	}

public:
	_xvector_base()
	{
		NOTIMPL
	}

private: 
	_xvector_base(const _xvector_base<T> &)
	{
		NOTIMPL
	}

public: 
	static const XUINT32 DefaultStartSize = 8;

	typedef XUINT32 (* PFNGrowthPolicyFunction)(XUINT32);

protected: 
	XUINT8 * m_data;
protected: 
	XUINT32 m_count;
	XUINT32 m_actualSize;
	XUINT32 m_StartOffset;
	PFNGrowthPolicyFunction m_GrowthPolicyFunction;
	bool m_isContainingWeakPointers;

public: 
	typedef typename _xvector_iterator<T> iterator;
	typedef typename _xvector_const_iterator<T> const_iterator ;
	typedef typename _xvector_reverse_iterator<T> reverse_iterator;
	typedef typename _xvector_const_reverse_iterator<T> const_reverse_iterator;

public: 
	static XUINT32 __stdcall DefaultGrowthPolicy(XUINT32 oldSize)
	{
		unsigned int result;

		if ( oldSize >= 8 )
			result = 2 * oldSize;
		else
			result = 8;
		return result;
	}

	~_xvector_base()
	{
		this->clear();
	}

	void set_growth_policy(PFNGrowthPolicyFunction _Ptr)
	{
		this->m_GrowthPolicyFunction = _Ptr;
	}

	XUINT32 size() const
	{
		return this->m_count;
	}

	XUINT32 empty()
	{
		return this->size() == 0;
	}

	XUINT32 capacity() const
	{
		return this->m_actualSize;
	}

	HRESULT front(T & val)
	{
		return this->get_item(0,val);
	}

	HRESULT back(T & val)
	{
		HRESULT hr = S_OK;

		if ( this->m_count )
			hr = this->get_item(this->m_count - 1, val);
		else
			hr = 0x80004005u;
		return hr;
	}

	HRESULT get_item(XUINT32 index, T & val)
	{
		HRESULT hr = S_OK;

		XASSERT_SUCCESSED(this->CheckRange(index));

		val = *this->TypedPointerAt(index);

		return hr;
	}

	HRESULT set_item(XUINT32 index, const T & val)
	{
		HRESULT hr = S_OK;

		XASSERT_SUCCESSED(this->CheckRange(index));
		this->DestroyAt(index);
		this->ConstructAt(index,val);

		return hr;
	}

	HRESULT reserve(XUINT32 requestedSize)
	{
		HRESULT hr = S_OK;

		XASSERT_SUCCESSED(this->ExpandStorageIfNeeded(requestedSize));


		return hr;
	}

	HRESULT fill_back(const T & val, XUINT32 count)
	{
		HRESULT hr = S_OK;
		XUINT32 oldCount = this->m_count;

		XASSERT_SUCCESSED(this->ExpandStorageIfNeeded(this->m_count + count));

		this->m_count += count;

		XASSERT_SUCCESSED(this->ConstructRange(oldCount, count, val));


		return hr;
	}

	HRESULT grow_back(XUINT32 count)
	{
		HRESULT hr = S_OK;

		XUINT32 newCount = count + this->m_count;
		XASSERT_SUCCESSED(this->ExpandStorageIfNeeded(newCount));
		this->m_count = newCount;

		return hr;
	}

	HRESULT push_back(const T & val)
	{
		HRESULT hr = S_OK;

		XASSERT_SUCCESSED(this->ExpandStorageIfNeeded());

		auto dd = this->m_count++;
		this->ConstructAt(dd,val);

		return hr;
	}

	HRESULT pop_back()
	{
		HRESULT hr = S_OK;

		if ( this->m_count )
			hr = this->erase(this->m_count - 1);
		else
			hr = 0x80004005u;
		return hr;
	}

	HRESULT resize(XUINT32)
	{
		NOTIMPL
	}

	HRESULT insert(class _xvector_const_iterator<T> it, const T & val)
	{
		HRESULT hr = S_OK;

		if ( it.IsEndIterator() )
			hr = this->push_back(val);
		else
			hr = this->insert(it.m_Upto, val);
		return hr;
	}

	HRESULT insert(XUINT32 index, const T & val)
	{
		HRESULT hr = S_OK;

		XASSERT_WITH_HR(index <= this->m_count,0x8000FFFFu);

		XASSERT_SUCCESSED(this->ExpandStorageIfNeeded());
		XASSERT_SUCCESSED(this->MoveRange(index + 1, index, this->m_count - index));

		this->ConstructAt(index,val);
		++this->m_count;

		return hr;
	}

	HRESULT erase(class _xvector_base_const_iterator<T> it, XUINT32)
	{
		NOTIMPL
	}

	HRESULT erase(class _xvector_base_const_iterator<T> it)
	{
		HRESULT hr = S_OK;

		if ( it.IsEndIterator() )
			hr = 0x80004005u;
		else
			hr = this->erase(it.m_Upto, 1u);
		return hr;
	}

	HRESULT erase(XUINT32 index, XUINT32 numberOfItems)
	{
		HRESULT hr = S_OK;

		XASSERT_SUCCESSED(this->CheckRange(index, numberOfItems));
		XASSERT_SUCCESSED(this->MoveRange(index, index + numberOfItems, this->m_count - index - numberOfItems));
		this->m_count -= numberOfItems;

		return hr;
	}

	HRESULT erase(XUINT32 index)
	{
		return this->erase(index, 1);
	}

	void clear()
	{
		this->DestroyRange(0,this->m_count);
		delete[] this->m_data;
		this->ResetMembers();
	}

	void XcpMarkWeakPointers()
	{
		XASSERT(this->m_data == NULL);
		XASSERT(this->m_count == 0);
		this->m_isContainingWeakPointers = TRUE;
	}

protected: 
	HRESULT CheckRange(XUINT32 startIndex, XUINT32 count) const
	{
		NOTIMPL
	}

	HRESULT CheckRange(XUINT32 index)
	{
		return index < this->m_count ? 0 : 0x80004005u;
	}

	XUINT32 SizeOfNItems(XUINT32 numberOfItems) const
	{
		return DefaultStartSize * numberOfItems;
	}

	void ConstructAt(XUINT32 index, const T & val)
	{
		void *buffer = this->RawPointerAt(index);
		new (buffer)T();
	}

	void DestroyAt(XUINT32 index)
	{
		delete this->TypedPointerAt(index);
	}

	T * TypedPointerAt(XUINT32 index) const
	{
		return static_cast<T*>((void *)this->RawPointerAt(index));
	}

	XUINT8 * CreateRawBuffer(XUINT32 numberOfItems) const
	{
		 void *rawBuffer = new XUINT8[this->SizeOfNItems(numberOfItems)];
		 if(this->m_isContainingWeakPointers)
		 {
			 XcpMarkWeakPointer(rawBuffer, (void *)0xFFFFFFFF);

		 }
		 return (XUINT8 *)rawBuffer;
	}

	XUINT8 * RawPointerAt(XUINT32 index) const
	{
		XASSERT(index < capacity());
		XASSERT(this->m_data != NULL);

		return this->m_data + this->SizeOfNItems(this->WrappedOffset(index));
	}

	void ResetMembers()
	{
		NOTIMPL
	}

	XUINT32 ExpandedBufferSize()
	{
		NOTIMPL
	}

	HRESULT ExpandStorageIfNeeded(XUINT32 newBufferSize)
	{
		HRESULT hr = S_OK;

		if ( this->m_data )
		{
			if ( newBufferSize > this->m_actualSize )
			{
				XASSERT_SUCCESSED(this->ResizeBuffer(newBufferSize));
			}
		}
		else
		{
			XASSERT(this->m_count == 0);
			XASSERT_NOTNULL_WITH_HR(this->m_data = this->CreateRawBuffer(newBufferSize),0x8007000Eu);
			this->m_actualSize = newBufferSize;
		}
		return hr;
	}

	HRESULT ExpandStorageIfNeeded()
	{
		HRESULT hr = S_OK;

		if(this->m_data)
		{

			if(this->m_count == this->capacity())
			{
				XASSERT_SUCCESSED(this->ExpandStorageIfNeeded(this->ExpandedBufferSize()))
			}
		}
		else
		{
			XASSERT_SUCCESSED(this->ExpandStorageIfNeeded(DefaultStartSize));
		}

		return hr;

	}

	HRESULT ResizeBuffer(XUINT32 newBufferSize)
	{
		HRESULT hr = S_OK;
		XUINT32 newBuffer = 0;

		if(newBufferSize >= this->m_count)
		{

			if ( newBufferSize > this->m_count )
			{
				void *newBuffer;

				XASSERT_NOTNULL_WITH_HR(newBuffer = this->CreateRawBuffer(newBufferSize),0x8007000Eu);

				if((this->m_count + this->m_StartOffset) > this->m_actualSize)
				{
					memcpy(newBuffer, &this->m_data[this->SizeOfNItems(this->m_StartOffset)], this->SizeOfNItems(this->m_actualSize - this->m_StartOffset));

					memcpy((void *)((XUINT8 *)newBuffer + this->SizeOfNItems(this->m_actualSize - this->m_StartOffset)), this->m_data, this->SizeOfNItems(this->m_StartOffset + this->m_count - this->m_actualSize));

				}
				else
				{
					memcpy(newBuffer, &this->m_data[this->SizeOfNItems(this->m_StartOffset)], this->SizeOfNItems(this->m_count));
				}

				this->m_StartOffset = 0;

				delete[] this->m_data;
				this->m_data = (XUINT8 *)newBuffer;
				this->m_actualSize = newBufferSize;



			}
		}
		else
		{
			XASSERT_SUCCESSED(DestroyRange(newBufferSize, this->m_count - newBufferSize));
		}

		return hr;
	}

	HRESULT ConstructRange(XUINT32 startIndex, XUINT32 count, const T & val)
	{
		HRESULT hr = S_OK;

		XASSERT_SUCCESSED(this->CheckRange(startIndex, count));
		XUINT32 i;

		for ( i = startIndex; i < count + startIndex; ++i )
			this->ConstructAt(i,val);

		return hr;
	}

	HRESULT DestroyRange(XUINT32 startIndex, XUINT32 count)
	{
		HRESULT hr = S_OK;

		XASSERT_SUCCESSED(this->CheckRange(startIndex, count));
		XUINT32 i;
		for ( i = startIndex; i < count + startIndex; ++i )
			this->DestroyAt(i);

		return hr;
	}

	XUINT32 WrappedOffset(XUINT32 uIndex) const
	{
		XUINT32 result;
		XUINT32 offsetPlusIndex;
		if ( this->m_StartOffset )
		{

			XASSERT(this->m_actualSize > this->m_StartOffset);
			XASSERT(this->m_actualSize > uIndex);

			offsetPlusIndex = uIndex + this->m_StartOffset;
			if ( offsetPlusIndex >= this->m_actualSize )
				result = offsetPlusIndex - this->m_actualSize;
			else
				result = offsetPlusIndex;
		}
		else
		{
			result = uIndex;
		}

		return result;
	}

	HRESULT MoveRange(XUINT32 destIndex, XUINT32 sourceIndex, XUINT32 count)
	{
		HRESULT hr = S_OK;

		if ( sourceIndex <= destIndex )
		{
			if ( sourceIndex < destIndex )
			{
				if ( count )
				{
					memmove(this->RawPointerAt(destIndex), this->RawPointerAt(sourceIndex), this->SizeOfNItems(count));
				}
			}
		}
		else
		{

			XASSERT_SUCCESSED(this->DestroyRange(destIndex, sourceIndex - destIndex));
			if ( count )
			{
				if ( this->m_StartOffset )
				{
					hr = 0x8000FFFFu;
				}
				else
				{
					memcpy(this->RawPointerAt(destIndex), this->RawPointerAt(sourceIndex), this->SizeOfNItems(count));
				}
			}
		}
		return hr;
	}

	XUINT32 IsContiguous(XUINT32, XUINT32)
	{
		NOTIMPL
	}

};





